package concurrent.c_012_ThreadPool;

import lombok.AllArgsConstructor;
import util.TimeUtil;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 使用 Future 进行异步编程缺点：不知道何时结束，阻塞获取结果
 * Future 接口封装了取消，获取线程结果，以及状态判断是否取消，是否完成这几个方法，都很有用。
 */
public class T06_Future {
    // 定义一个线程池，方便开启和执行多线程，此处为了方便，直接使用 newFixedThreadPool
    static ExecutorService exs = Executors.newFixedThreadPool(10);

    /**
     * 使用 Futrue 来实现多线程执行归集操作
     * 使用线程池提交 Callable 接口任务，返回 Future 接口，添加进 list，最后遍历该 List 且内部使用 while 轮询，并发获取结果
     */
    public static void main(String[] args) {
        long start = Instant.now().toEpochMilli();
        // 结果集装载在 list 里面
        List<Integer> list = new ArrayList<>();
        List<Future<Integer>> futureList = new ArrayList<>();
        try {
            // 1.高速提交 10 个任务，每个任务返回一个 Future 入 futureList 装载起来，这样 10 个线程就并行去处理和计算了
            for (int i = 0; i < 10; i++) {
                futureList.add(exs.submit(new CallableTask(i + 1)));
            }
            long getResultStart = Instant.now().toEpochMilli();
            System.out.println("结果归集开始时间：" + TimeUtil.nowDateTime());

            // 2.结果归集，用迭代器遍历 futureList，高速轮询（模拟实现了并发），任务完成就移除
            while (futureList.size() > 0) {
                Iterator<Future<Integer>> iterable = futureList.iterator();
                while (iterable.hasNext()) { // 遍历轮询
                    Future<Integer> future = iterable.next();
                    // 如果任务完成就立马取结果，并且，并且把该任务直接从 futureList 移除掉，否则判断下一个任务是否完成
                    if (future.isDone() && !future.isCancelled()) {
                        Integer i = future.get(); // 获取结果
                        list.add(i); // 把结果装入进去
                        iterable.remove(); // 把 futrue 任务移除
                        System.out.println("任务 i = " + i + " 获取完成，移出任务队列！");
                    } else {
                        Thread.sleep(1); // 避免 CPU 高速运转（这就是轮询的弊端），这里休息 1 毫秒，CPU 纳秒级别
                    }
                }
            }
            System.out.println("任务处理结果 list = " + list);
            System.out.println("总耗时 = " + (System.currentTimeMillis() - start) + "，取结果归集耗时 = " + (System.currentTimeMillis() - getResultStart));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            exs.shutdown();
        }
    }

    // 任务采用 sleep 模拟处理任务需要消耗的时间
    @AllArgsConstructor
    static class CallableTask implements Callable<Integer> {
        Integer i; // 用来编号任务，方便日志里输出识别

        @Override
        public Integer call() throws Exception {
            if (i == 1) {
                Thread.sleep(3000); // 任务 1 耗时 3 秒
            } else if (i == 5) {
                Thread.sleep(5000); // 任务 5 耗时 5 秒
            } else {
                Thread.sleep(1000); // 其它任务耗时 1 秒
            }
            System.out.println("task 线程：" + Thread.currentThread().getName() + " 任务 i =" + i + "，完成！");
            return i;
        }
    }
}
